/*
 * 客户端与服务器通讯协议相关的帮助类
 * Copyright (C) 2009  YuLicheng
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.sysmo.communication;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 *
 * 通讯协议帮助类。此类按照通讯协议的要求，将部分常用的协议功能打包实现。
 *
 * @author YuLicheng
 */
public class CommunicationProtocol {

    /**
     * 根节点标签名。
     */
    public static final String ROOT_TAG = "request";

    /**
     * 协议版本号。
     */
    public static final String CURRENT_VERSION = "1.0";

    /**
     * 请求类型数目。合法的类型号为从0到此数目减一。
     */
    public final static int CLIENT_EVENT_NUMBER = 18;

    /**
     * 使用协议封装请求消息。此方法会得到如下的内容：
     *
     * <request version="1.0" type="type">
     *     content
     * </request>
     *
     * @param type 请求类型号。
     * @param content 附加的信息。
     * @return 协议字符串。
     */
    public static String encapsulate(int type, String content) {
        return "<" + ROOT_TAG + " version=\"" + CURRENT_VERSION 
                + "\" type=\"" + type + "\">"
                + content + "</" + ROOT_TAG + ">";
    }

    /**
     * 使用协议封装带有监控项ID的消息。此方法会得到如下的内容：
     *
     * <request version="1.0" type="type">
     *     <itemID>itemID</itemID>
     *     content
     * </request>
     *
     * @param type 请求类型。
     * @param itemID 监控项ID。
     * @param content 附加的信息。
     * @return 协议字符串。
     */
    public static String encapsulateWithItemID(int type, int itemID,
            String content) {
        return encapsulate(type, "<itemID>" + itemID + "</itemID>" + content);
    }

    /**
     * 将XML字符串转换为XML Document对象。
     *
     * @param string 要转换的字符串，其中为XML文档文本。
     * @return 对应的Docuemnt对象
     * @throws org.w3c.dom.DOMException
     * @throws java.io.IOException
     * @throws javax.xml.parsers.ParserConfigurationException
     * @throws org.xml.sax.SAXException
     */
    public static Document getDocumentFromString(String string)
            throws DOMException, IOException, ParserConfigurationException,
            SAXException {
        InputStream is = new ByteArrayInputStream(string.getBytes("utf-8"));
        return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
    }

    /**
     * 获取消息中的请求类型号。
     *
     * @param doc 消息对应的Document对象。
     * @return 请求类型号。
     * @throws IllegalMessageTypeException 消息中不含请求类型号或请求类型号非法。
     */
    public static int getType(Document doc) throws IllegalMessageTypeException {
        int type = -1;
        try {
            Node node = doc.getFirstChild().getAttributes().getNamedItem("type");
            if (node != null) {
                type = Integer.parseInt(node.getNodeValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (type < 0 || type >= CommunicationProtocol.CLIENT_EVENT_NUMBER) {
            throw new IllegalMessageTypeException(type);
        }
        return type;
    }

    /**
     * 获取消息中的监控项ID。
     *
     * @param doc 消息对应的Document对象。
     * @return 监控项ID。
     * @throws IllegalItemIDException 消息中不含监控项ID
     */
    public static int getItemID(Document doc) throws IllegalItemIDException {
        int iid = -1;
        try {
            NodeList nodes = doc.getElementsByTagName("itemID");
            if (nodes.getLength() > 0) {
                iid = Integer.parseInt(nodes.item(0).getFirstChild().getNodeValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (iid < 0) {
            throw new IllegalItemIDException();
        }
        return iid;
    }
}
